#include <gtest/gtest.h>
#include <gmock/gmock.h>

/*
Mocking Free Functions
It is not possible to directly mock a free function (i.e. a C-style function or a static method). 
If you need to, you can rewrite your code to use an interface (abstract class).

Instead of calling a free function (say, OpenFile) directly, 
introduce an interface for it and have a concrete subclass that calls the free function:

class FileInterface {
 public:
  ...
  virtual bool Open(const char* path, const char* mode) = 0;
};

class File : public FileInterface {
 public:
  ...
  bool Open(const char* path, const char* mode) override {
     return OpenFile(path, mode);
  }
};

Your code should talk to FileInterface to open a file. Now it’s easy to mock out the function.

This may seem like a lot of hassle, but in practice you often have multiple related functions that you can put in the same interface, 
so the per-function syntactic overhead will be much lower.

If you are concerned about the performance overhead incurred by virtual functions, and profiling confirms your concern, 
you can combine this with the recipe for mocking non-virtual methods.
*/

int getX()
{
    return 0;
}

int getY()
{
    return 0;
}

int getZ()
{
    return 0;
}

template<typename T>
T set_get(T t)
{
    return t;
}

template<typename T>
class Get {
public:
    virtual int getX() = 0;
    virtual int getY() = 0;
    virtual int getZ() = 0;
    T set_get(T t);
};

template<typename T>
class MockGet : public Get<T> {
public:
    MOCK_METHOD(int, getX, (), (override));
    MOCK_METHOD(int, getY, (), (override));
    MOCK_METHOD(int, getZ, (), (override));
    MOCK_METHOD(T, set_get, (T t));
};

TEST(sample, free_function)
{
    MockGet<int> get;
    EXPECT_CALL(get, getX())
        .WillOnce(testing::Return(1));
    EXPECT_CALL(get, getY())
        .WillOnce(testing::Return(2));
    EXPECT_CALL(get, getZ())
        .WillOnce(testing::Return(3));

    EXPECT_EQ(1, get.getX());
    EXPECT_EQ(2, get.getY());
    EXPECT_EQ(3, get.getZ());
}

TEST(sample, free_template_function)
{
    MockGet<int> get;
    EXPECT_CALL(get, set_get(1))
        .WillOnce(testing::Return(2));

    EXPECT_EQ(2, get.set_get(1));
}

int main()
{
    ::testing::InitGoogleTest();
    return RUN_ALL_TESTS();
}